package controller.arithmethicParser;

import java.io.IOException;
import java.io.Reader;

import model.ParseError;

/**
 * @author Sjaak Smetsers
 * @version 1.0 -- 06-03-2012
 */
public class ArithmeticTokenizer {
	public enum ArithmeticToken {
		NumberToken("<number>"), PlusToken("+"), MinusToken("-"), DivToken("/"), TimesToken("*"), EOFToken("<EOF>");
		
		private String	tokenString;
		
		ArithmeticToken(String str) {
			tokenString = str;
		}
		
		@Override
		public String toString() {
			return tokenString;
		}
	}
	
	private static final int	EOF_CHAR	= -1;
	private Reader				input;
	private int					currentChar;
	
	private ArithmeticToken		currentToken;
	
	private int					numberToken;
	
	public ArithmeticTokenizer(Reader input) throws ParseError {
		this.input = input;
		try {
			currentChar = input.read();
			skipLayout();
		} catch (IOException ex) {
			throw new ParseError("IOException: " + ex);
		}
		nextToken();
	}
	
	public void nextToken() throws ParseError {
		try {
			scan();
			skipLayout();
		} catch (IOException ex) {
			throw new ParseError("IOException: " + ex);
		}
	}
	
	public ArithmeticToken currentToken() {
		return currentToken;
	}
	
	public int getNumber() throws ParseError {
		if (currentToken == ArithmeticToken.NumberToken) {
			return numberToken;
		} else {
			throw new ParseError("No number available for: " + currentToken);
		}
	}
	
	private void skipLayout() throws IOException {
		while (currentChar != EOF_CHAR && Character.isWhitespace(currentChar)) {
			currentChar = input.read();
		}
	}
	
	private void scan() throws IOException, ParseError {
		if (currentChar == EOF_CHAR) {
			currentToken = ArithmeticToken.EOFToken;
		} else {
			switch (currentChar) {
				case '+':
					currentChar = input.read();
					currentToken = ArithmeticToken.PlusToken;
					break;
				case '-':
					currentChar = input.read();
					currentToken = ArithmeticToken.MinusToken;
					break;
				case '*':
					currentChar = input.read();
					currentToken = ArithmeticToken.TimesToken;
					break;
				case '/':
					currentChar = input.read();
					currentToken = ArithmeticToken.DivToken;
					break;
				default:
					if (Character.isDigit(currentChar)) {
						scanNumber();
					} else {
						throw new ParseError("Illegal input character");
					}
			}
		}
	}
	
	private void scanNumber() throws IOException {
		numberToken = currentChar - '0';
		currentToken = ArithmeticToken.NumberToken;
		
		for (currentChar = input.read(); Character.isDigit(currentChar); currentChar = input.read()) {
			numberToken = numberToken * 10 + Character.digit(currentChar, 10);
		}
	}
	
}
